package br.com.duduso.conferencia_claro_desktop.services;

import br.com.duduso.conferencia_claro_desktop.entity.Conferencia;
import br.com.duduso.conferencia_claro_desktop.entity.Detalhe;
import br.com.duduso.conferencia_claro_desktop.enums.EnvioEmail;
import br.com.duduso.conferencia_claro_desktop.utils.EmailUtils;
import br.com.duduso.conferencia_claro_desktop.utils.NumberUtils;
import br.com.duduso.conferencia_claro_desktop.utils.ResourceUtils;
import br.com.duduso.conferencia_claro_desktop.utils.ZipUtils;
import java.awt.Desktop;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.Normalizer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import jxl.Sheet;
import jxl.Workbook;
import jxl.WorkbookSettings;
import jxl.read.biff.BiffException;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.view.JasperViewer;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.mail.EmailAttachment;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.MultiPartEmail;

/**
 *
 * @author duduso
 */
public class ConferenciasService {

        private enum CONDICOES_DESCONTO {

                PACOTE_INTERNET("Pacote Internet"),
                DADOS_GPRS("Dados GPRS"),
                GPRS("GPRS");

                private String value;

                private CONDICOES_DESCONTO(String value) {
                        this.value = value;
                }

                public String getValue() {
                        return value;
                }

        }

        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");

        //PATH dos Arquivos a Serem Lidos/Escritos
        private String PATH_ARQUIVO_SAIDA = System.getProperty("java.io.tmpdir") +
                                            System.getProperty("file.separator") +
                                            "Contas_Conferidas_" +
                                            sdf.format(new Date()).replace("/", ".") + ".xls";

        //Arquivos a Serem Manipulados Pela Aplicação
        private File arquivoSaida;

        /**
         * Executa a Leitura dos Arquivos de Entrada - Formato Excel
         * Para Computar os Valores e Agregá-los à Lista de Conferências
         */
        public void processarArquivosEntrada(List<File> arquivosEntrada, File arquivoTotal){

                this.arquivoSaida = new File(PATH_ARQUIVO_SAIDA);
                this.arquivoSaida.deleteOnExit();

                List<Conferencia> conferencias = new ArrayList<Conferencia>();

                for(File fileInput : arquivosEntrada){
                        try {
                                lerArquivo(fileInput, conferencias);
                        } catch (IOException ex) {
                                Logger.getLogger(ConferenciasService.class.getName()).log(Level.SEVERE, null, ex);
                        } catch (BiffException ex) {
                                Logger.getLogger(ConferenciasService.class.getName()).log(Level.SEVERE, null, ex);
                        } catch (java.text.ParseException ex) {
                                Logger.getLogger(ConferenciasService.class.getName()).log(Level.SEVERE, null, ex);
                        }
                }

                try {
                        gravaConteudoTotalizado(conferencias, arquivoTotal);
                } catch (IOException ex) {
                        Logger.getLogger(ConferenciasService.class.getName()).log(Level.SEVERE, null, ex);
                } catch (WriteException ex) {
                        Logger.getLogger(ConferenciasService.class.getName()).log(Level.SEVERE, null, ex);
                }

        }

        /**
         * Promove a Leitura dos Arquivos de Entrada (Formato Excel - 97 à 2003)
         * Planilha de Dados de Contas da Operadora Claro
         */
        private void lerArquivo(File arquivoEntrada, List<Conferencia> conferencias)
                throws IOException, BiffException, java.text.ParseException{
                Workbook workbook = Workbook.getWorkbook(arquivoEntrada);
                Sheet planilha    = workbook.getSheet(0);
                int linhas        = planilha.getRows();
                for(int i = 0; i < linhas; i++){
                        String telefone  = planilha.getCell(0, i).getContents();
                        String descricao = planilha.getCell(1, i).getContents();
                        String valor     = planilha.getCell(8, i).getContents();
                        if(isCondicoesValidas(telefone, valor) && !isCondicaoDesconto(descricao)) {
                                    Conferencia conferencia = new Conferencia();
                                    conferencia.setTelefone(telefone);
                                    conferencia.setDescricaoServico(descricao);
                                    conferencia.setValorCobrado(NumberUtils.getDoubleNumber(valor));
                                    if(conferencias.contains(conferencia)){
                                            Conferencia conf = conferencias.get(conferencias.indexOf(conferencia));
                                            conf.setValorCobrado(conf.getValorCobrado() + conferencia.getValorCobrado());
                                    }else{
                                            conferencias.add(conferencia);
                                    }
                        }
                }
                workbook.close();
        }

        /**
         * Verifica se o Telefone ou o Valor a Ser Pago Estão Nulos
         * e Também Atendam a Outras Condições de Preenchimento
         */
        private boolean isCondicoesValidas(String telefone, String valor) {
                if(telefone == null || telefone.length() == 0
                        || telefone.length() != 12) {
                        return false;
                }
                if(valor == null || valor.length() == 0
                        || valor.equals("0")) {
                        return false;
                }
                return true;
        }

        /**
         * Verifica se o Serviço Prestado Está Incluso Em Condições
         * Não Computáveis, Como Pacote Internet e Dados GPRS
         */
        private boolean isCondicaoDesconto(String descricaoServico) {
                List<String> condicoesDesconto = new ArrayList<String>();
                condicoesDesconto.add(CONDICOES_DESCONTO.PACOTE_INTERNET.getValue());
                condicoesDesconto.add(CONDICOES_DESCONTO.DADOS_GPRS.getValue());
                condicoesDesconto.add(CONDICOES_DESCONTO.GPRS.getValue());
                for(String s : condicoesDesconto){
                        if(descricaoServico.toUpperCase().contains(s.toUpperCase())){
                                return true;
                        }
                }
                return false;
        }

        /**
         * Grava o Conteúdo Totalizado em Uma Nova Planilha, Com Base Em Uma
         * Planilha Modelo
         */
        private void gravaConteudoTotalizado(List<Conferencia> dadosTotalizados, File arquivoTotal)
                throws IOException, WriteException {
                WritableWorkbook arquivoEscrita = null;
                try {
                        WorkbookSettings ws = new WorkbookSettings();
                        ws.setLocale(new Locale("pt", "BR"));
                        Workbook workbook = Workbook.getWorkbook(arquivoTotal, ws);
                        arquivoEscrita = Workbook.createWorkbook(arquivoSaida, workbook);
                        WritableSheet writableSheet = arquivoEscrita.getSheet(0);
                        for(Conferencia pojo : dadosTotalizados){
                                    configurarValorCelulaParaGravacao(pojo, arquivoEscrita, writableSheet);
                        }
                } catch (Exception e) {
                        e.printStackTrace();
                } finally {
                        arquivoEscrita.write();
                        arquivoEscrita.close();
                }
        }

        /**
         * Configura os Valores nas Células Para Gravação na Nova Planilha de
         * Totais
         */
        private void configurarValorCelulaParaGravacao(Conferencia conferencia, WritableWorkbook arquivoExcel,
                WritableSheet writableSheet)throws WriteException, ParseException{
                StringBuilder telefone = new StringBuilder("(");
                char[] charsTelefone = conferencia.getTelefone().toCharArray();
                for(int i = 0; i < charsTelefone.length; i++){
                        if(i == 2){
                                telefone.append(")");
                        }else{
                                telefone.append(Character.toString(charsTelefone[i]));
                        }
                }
                Sheet planilha = arquivoExcel.getSheet(0);
                int linhas = planilha.getRows();
                for(int i = 0; i < linhas; i++){
                        String numTel = planilha.getCell(1, i).getContents();
                        if(numTel.equals(telefone.toString())){
                                Double valorCobrado = new Double(conferencia.getValorCobrado());
                                jxl.format.CellFormat cellFormat = planilha.getCell(5, i).getCellFormat();
                                jxl.write.Number number = new jxl.write.Number(5, i, valorCobrado, cellFormat);
                                writableSheet.addCell(number);
                        }
                }
        }

        public void abrirArquivoSaida(){
                try {
                            Desktop.getDesktop().open(arquivoSaida);
                    } catch (Exception e) {
                            e.printStackTrace();
                    }
        }

        public List<Object[]> obterListaClientes(File arquivoTotais)
                throws Exception{
                List<Object[]> values = new ArrayList<Object[]>();
                Workbook workbook = Workbook.getWorkbook(arquivoTotais);
                Sheet planilha    = workbook.getSheet(0);
                int linhas        = planilha.getRows();
                for(int i = 0; i < linhas; i++){
                        String cliente = planilha.getCell(0, i).getContents();
                        String telefone = planilha.getCell(1, i).getContents();
                        String email = planilha.getCell(2, i).getContents();
                        if(!StringUtils.isBlank(cliente)
                                && !StringUtils.isBlank(email)
                                && !cliente.equalsIgnoreCase("NOME")){
                                String valorDesconto = planilha.getCell(4, i).getContents();
                                String teste2 = (valorDesconto != null && valorDesconto.length() > 0)
                                              ? valorDesconto.substring(5, valorDesconto.length()) : null;
                                String desconto = NumberUtils.getStringDouble(teste2);
                                Object[] value = new Object[4] ;
                                value[0] = cliente;
                                value[1] = telefone;
                                value[2] = email;
                                value[3] = desconto != null ? desconto : "0.0";
                                values.add(value);
                        }
                }
                return values;
        }

        public void detalharCliente(Object[] clienteSelecionado, List<File> arquivosEntrada)
                throws IOException, BiffException, ParseException, JRException, Exception{
                List<Detalhe> detalhes = obterDetalhamentoCliente(clienteSelecionado, arquivosEntrada);
                Collections.sort(detalhes);
                JasperPrint print = gerarDetalhesJasper(detalhes, clienteSelecionado);
                JasperViewer jrv = new JasperViewer(print);
                jrv.setVisible(true);
        }

        public void detalharEnviarClientes(List<Object[]> clientesSelecionados, List<File> arquivosEntrada)
                throws Exception{
                try {
                        for(Object[] cliente : clientesSelecionados){
                                detalharEnviarCliente(cliente, arquivosEntrada);
                        }
                } catch (Exception e) {
                        e.printStackTrace();
                        throw e;
                }
        }

        public void detalharEnviarCliente(Object[] clienteSelecionado, List<File> arquivosEntrada)
                throws IOException, BiffException, ParseException, JRException, EmailException, Exception{
                List<Detalhe> detalhes = obterDetalhamentoCliente(clienteSelecionado, arquivosEntrada);
                if(detalhes.isEmpty()){
                        return;
                }
                Collections.sort(detalhes);
                String nomeCliente = normalizarString(((String) clienteSelecionado[0]).replace(" ", "_").replace("/", "."));
                String telefone = (String) clienteSelecionado[1];
                String informacaoEmail = "Conta Detalhada - ".concat(nomeCliente).concat(" - ").concat(telefone);
                String arquivoDestino = System.getProperty("java.io.tmpdir")
                        .concat(System.getProperty("file.separator"))
                        .concat("Conta_Detalhada_")
                        .concat(nomeCliente)
                        .concat("_").concat(telefone).concat(".pdf");
                JasperPrint print = gerarDetalhesJasper(detalhes, clienteSelecionado);
                JasperExportManager.exportReportToPdfFile(print, arquivoDestino);
                MultiPartEmail yahoo = EmailUtils.getEmailYahoo(
                        "duduso2004@yahoo.com.br",
                        "Eduardo Santos de Oliveira",
                        informacaoEmail,
                        informacaoEmail);
                EmailAttachment anexo = EmailUtils.getAnexo(
                        arquivoDestino,
                        informacaoEmail,
                        informacaoEmail);
                yahoo.attach(anexo);
                yahoo.send();
                File file = new File(arquivoDestino);
                file.delete();
        }

        private List<Detalhe> obterDetalhamentoCliente(Object[] clienteSelecionado,
                List<File> arquivosEntrada) throws Exception{
                List<Detalhe> detalhes = new ArrayList<Detalhe>();
                for(File file : arquivosEntrada){
                        gerarDetalhamento(clienteSelecionado, detalhes, file);
                }
                return detalhes;
        }

        private void gerarDetalhamento(Object[] clienteSelecionado, List<Detalhe> detalhes,
                File arquivoEntrada) throws Exception {
                Workbook workbook = Workbook.getWorkbook(arquivoEntrada);
                Sheet planilha    = workbook.getSheet(0);
                int linhas        = planilha.getRows();
                for(int i = 0; i < linhas; i++){
                        String telefone  = planilha.getCell(0, i).getContents();
                        String descricao = planilha.getCell(1, i).getContents();
                        String valor     = planilha.getCell(8, i).getContents();
                        String telClienteSelecionado = clienteSelecionado[1].toString().replace("(", "").replace(")", " ");
                        if(telefone.equals(telClienteSelecionado)
                                && isCondicoesValidas(telefone, valor)
                                && !isCondicaoDesconto(descricao)){
                                detalhes.add(getDetalhe(planilha, i, clienteSelecionado));
                        }
                }
        }

        private Detalhe getDetalhe(Sheet planilha, int index, Object[] clienteSelecionado)
                throws Exception{
                Detalhe detalhe = new Detalhe();
                detalhe.setTelefone(planilha.getCell(0, index).getContents());
                detalhe.setCliente((String) clienteSelecionado[0]);
                detalhe.setDescricao(planilha.getCell(1, index).getContents());
                detalhe.setDataLigacao(planilha.getCell(2, index).getContents().trim());
                detalhe.setHora(planilha.getCell(3, index).getContents());
                detalhe.setOrigemDestino(planilha.getCell(4, index).getContents());
                detalhe.setNumeroDiscado(planilha.getCell(5, index).getContents());
                detalhe.setDuracaoLigacao(planilha.getCell(6, index).getContents());
                detalhe.setTarifa(NumberUtils.getStringDouble(planilha.getCell(7, index).getContents()));
                detalhe.setValorLigacao(NumberUtils.getStringDouble(planilha.getCell(8, index).getContents()));
                return detalhe;
        }

        private JasperPrint gerarDetalhesJasper(List<Detalhe> detalhes, Object[] cliente)
                throws Exception{

                try {

                        String pathLogoClaro = System.getProperty("java.io.tmpdir") + File.separator + "claro.jpg";

                        File image = new File(pathLogoClaro);
                        
                        if(!image.exists()){
                                BufferedImage imagem = ImageIO.read(ResourceUtils.getStream("/images/claro.jpg"));
                                ImageIO.write(imagem, "JPG", new File(pathLogoClaro));
                        }

                        Map mapa = new HashMap();
                        mapa.put("cliente", cliente[0]);
                        mapa.put("telefone", cliente[1]);
                        mapa.put("desconto", cliente[3]);
                        mapa.put("logo_claro", pathLogoClaro);

                        JasperReport jasperReport = JasperCompileManager.compileReport(ResourceUtils.getStream("/reports/Detalhamento_Claro.jrxml"));
                        JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, mapa, new JRBeanCollectionDataSource(detalhes));

                        return jasperPrint;

                } catch (Exception e) {

                        JOptionPane.showMessageDialog(null, e);
                        throw e;

                }

        }

        public Map<Object[], List<File>> detalharEnviar(List<Object[]> clientesSelecionados, List<File> arquivosEntrada, 
                String opcaoEnvio) throws IOException, BiffException, ParseException, JRException, EmailException, Exception{
                Map<Object[], File> mapaArquivo = new LinkedHashMap<Object[], File>();
                try {
                
                for(Object[] cliente : clientesSelecionados){
                        detalharEnviarCliente(cliente, arquivosEntrada, mapaArquivo);
                }
                Map<Object[], List<File>> arquivosAgrupados = agruparArquivosPorCliente(mapaArquivo);
                for(Entry<Object[], List<File>> value : arquivosAgrupados.entrySet()){
                        String zipFile = System.getProperty("java.io.tmpdir").concat(File.separator)
                                .concat("Detalhamento_Contas_Claro_").concat(String.valueOf(value.getKey()[2]))
                                .concat(".zip");
                        ZipUtils.criarAquivoZip(zipFile, value.getValue());
                        enviarEmail(zipFile, value.getKey(), opcaoEnvio);
                        for(File file : value.getValue()){
                                file.delete();
                        }
                        new File(zipFile).delete();
                }
                return arquivosAgrupados;
                } catch (Exception e) {
                    e.printStackTrace();
                        return null;
                }
        }

        private void enviarEmail(String arquivoDestino, Object[] clienteSelecionado, String opcaoEnvio) throws EmailException{
                String informacaoEmail = "Detalhamento_Contas_Claro_".concat(String.valueOf(clienteSelecionado[2]));
                String[] emailDestino = getEmailDestino(clienteSelecionado, opcaoEnvio);
                EmailUtils.enviarEmailGmail(informacaoEmail,
                        informacaoEmail,
                        emailDestino[1], 
                        emailDestino[0],
                        arquivoDestino);
        }

        public String[] getEmailDestino(Object[] clienteSelecionado, String opcaoEnvio){

                String[] envioEmail = new String[2];

                String meuEmail = "elaine.oliveira@grupolagoa.com.br";
                String meuNome = "Elaine Oliveira";
                String emailColaborador =  String.valueOf(clienteSelecionado[2]);
                String nomeColaborador = String.valueOf(clienteSelecionado[0]);

                //Envio do Email Para Minha Conta Pessoal
                if(opcaoEnvio.equals(EnvioEmail.MEU_EMAIL.getValue())){
                        envioEmail[0] = meuNome;
                        envioEmail[1] = meuEmail;
                }
                
                //Envio do Email Para o Email do Colaborador
                else if(opcaoEnvio.equals(EnvioEmail.EMAIL_COLABORADOR.getValue())){
                        envioEmail[0] = nomeColaborador;
                        envioEmail[1] = emailColaborador;
                }

                //Envio do Email Para Ambos os Emails - Meu Email + Email Colaborador
                else{
                        envioEmail[0] = "";
                        envioEmail[1] = String.format("%s,%s", emailColaborador, meuEmail);
                }

                return envioEmail;

        }

        private void detalharEnviarCliente(Object[] clienteSelecionado, List<File> arquivosEntrada,
                Map<Object[], File> mapaArquivo) throws Exception{
                List<Detalhe> detalhes = obterDetalhamentoCliente(clienteSelecionado, arquivosEntrada);
                if(detalhes.isEmpty()){
                        return;
                }
                Collections.sort(detalhes);
                gerarArquivoDetalhes(detalhes, clienteSelecionado, mapaArquivo);
        }

        private void gerarArquivoDetalhes(List<Detalhe> detalhes, Object[] cliente,
                Map<Object[], File> mapArquivos) throws Exception{

                try {

                        String pathLogoClaro = System.getProperty("java.io.tmpdir") + File.separator + "claro.jpg";
                        File image = new File(pathLogoClaro);

                        if(!image.exists()){
                                BufferedImage imagem = ImageIO.read(ResourceUtils.getStream("/images/claro.jpg"));
                                ImageIO.write(imagem, "JPG", new File(pathLogoClaro));
                        }

                        Map mapa = new HashMap();
                        mapa.put("cliente", cliente[0]);
                        mapa.put("telefone", cliente[1]);
                        mapa.put("desconto", cliente[3]);
                        mapa.put("logo_claro", pathLogoClaro);

                        JasperReport jasperReport = JasperCompileManager.compileReport(ResourceUtils.getStream("/reports/Detalhamento_Claro.jrxml"));
                        JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, mapa, new JRBeanCollectionDataSource(detalhes));

                        String arquivoDestino = System.getProperty("java.io.tmpdir")
                                .concat(System.getProperty("file.separator"))
                                .concat("Conta_Detalhada_")
                                .concat(normalizarString(((String) cliente[0]).replace(" ", "_").replace("/", ".")))
                                .concat("_").concat((String) cliente[1]).concat(".pdf");

                        JasperExportManager.exportReportToPdfFile(jasperPrint, arquivoDestino);

                        mapArquivos.put(cliente, new File(arquivoDestino));

                } catch (Exception e) {

                        JOptionPane.showMessageDialog(null, e);
                        throw e;

                }

        }

        private Map<Object[], List<File>> agruparArquivosPorCliente(Map<Object[], File> mapa){
                Map<Object[], List<File>> mapaAgrupado = new LinkedHashMap<Object[], List<File>>();
                for(Entry<Object[], File> value : mapa.entrySet()){
                        if(containsKey(mapaAgrupado, value.getKey())){
                                for(Entry<Object[], List<File>> v : mapaAgrupado.entrySet()){
                                        if(arrayEquals(v.getKey(), value.getKey())){
                                                v.getValue().add(value.getValue());
                                        }
                                }
                        }else{
                                List<File> arquivos = new ArrayList<File>();
                                arquivos.add(value.getValue());
                                mapaAgrupado.put(value.getKey(), arquivos);
                        }
                }
                return mapaAgrupado;
        }

        /**
         * Verifica se o Mapa Contém Determinada Chave, Comparando as 
         * Chaves Através do E-mail do Cliente
         */
        private boolean containsKey(Map<Object[], List<File>> mapaAgrupado, Object[] value) {
                for(Entry<Object[], List<File>> entry : mapaAgrupado.entrySet()){
                        if(arrayEquals(entry.getKey(), value)){
                                return true;
                        }
                }
                return false;
        }

        /**
         * Compara se os Arrays de Object São Iguais, Considerando
         * o E-mail para Envio dos Arquivos de Detalhes
         * @param Object[] o1
         * @param Object[] o2
         * @return TRUE  - Caso o Email do Object[] o1 Seja Igual 
         * ao Email do Object[] o2
         *         FALSE - Caso Contrário
         */
        private boolean arrayEquals(Object[] o1, Object[] o2){
                return o1[2].equals(o2[2]);
        }

        /**
         * Método Para Regularizar Strings, Removendo as Acentuações
         * @param String param
         * @return String normalizada (sem acentuação)
         */
        public static String normalizarString(String param) {
                param = param.replaceAll(" ","_");
                param = Normalizer.normalize(param, Normalizer.Form.NFD);
                param = param.replaceAll("[^\\p{ASCII}]", "");
                return param;
        }

}
